package com.serverapk.reader;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.widget.EditText;

import com.google.gson.Gson;
import com.hcReader.AndroidUSB;
import com.serverapk.BaseApplication;
import com.serverapk.MainActivity;
import com.serverapk.PostNex;
import com.serverapk.R;
import com.serverapk.model.CachedThreadFactory;
import com.serverapk.model.CardData;
import com.serverapk.model.CardData1;
import com.serverapk.para.Common;
import com.serverapk.util.L;
import com.serverapk.util.ToastUtil;
import com.serverapk.util.Util;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * 读写器操作
 */

public class HcReaderUtil {

    private volatile static HcReaderUtil instance;

    private HcReaderUtil() {
    }

    public static HcReaderUtil getInstance() {
        if (instance == null) {
            synchronized (HcReaderUtil.class) {
                if (instance == null) {
                    instance = new HcReaderUtil();
                }
            }
        }
        return instance;
    }

    public void clear() {
        instance = null;
    }

    public Handler handler = new Handler();

    private UsbDevice usbDevice = null;
    private String Device_USB = "com.android.example.USB";
    private byte[] snr = new byte[4];


    /**
     * 表示的dmcode扇区的密码
     */
    private String dmcodePsd;

    /**
     * 表示的stpcode扇区的密码
     */
    private String stpcodePsd;

    /**
     * 表示存储扇区密码的IC卡扇区的代码，默认是2
     */
    private String dmcode;

    /**
     * 表示的是存放卡号，金额的扇区
     */
    private String stpcode;

    /**
     * 卡片的唯一序列号
     */
    private String cardSnr;

    private CardData cardData;
    private CardData1 cardData1;

    private Activity activity;

    /**
     * 读写器是否连接成功
     */
    private boolean isReaderSuccess = false;

    /**
     * 状态：区别当前是要取哪一扇区那一块的数据
     * 0：表示取第2扇区第一行数据（块号为8）
     * 1：表示取第1扇区第一行数据（块号为4）
     * 2：表示取第1扇区第二行数据（块号为5）
     * 3：表示需要读取的数据全部读取成功了
     */
    private int state = 0;

    /**
     * 卡号
     */
    private String cardNum;
    /**
     * 卡金额
     */
    private String cardMoney;
    /**
     * mqtt推过来的卡号
     */
    private String writeCardNum;
    /**
     * mqtt推过来的卡金额
     */
    private String writeCardMoney;

    /**
     * 是否写卡
     */
    private boolean isWriteCard = false;


    private MediaPlayer mediaPlayer;

    public void setwriteCardNum(String num) {
        writeCardNum = num;
    }

    public void setwriteCardMoney(String money) {
        writeCardMoney = money;
    }

    public void isWriteCard(boolean isWriteCard) {
        this.isWriteCard = isWriteCard;
    }

    /**
     * 初始化读写器
     */
    public void initHcReader(Activity activity) {
        this.activity = activity;
        BaseApplication.getInstance().manager = (UsbManager) activity.getSystemService(Context.USB_SERVICE);
        if (BaseApplication.getInstance().manager == null) {
            L.e("UsbManager is null.");
            ToastUtil.showLong(activity, "UsbManager is null");
            return;
        }
        try {
            BaseApplication.getInstance().reader = new AndroidUSB(BaseApplication.getInstance().manager);
        } catch (Exception e) {
            L.e("eeee=" + e.toString());
        }

        handler.postDelayed(runnable, 100);

    }

    private void request() {
        cardData = new CardData();
        cardData.setGuid(BaseApplication.getInstance().GUID);
        int time = (int) (System.currentTimeMillis() / 1000);
        String data = "guid=" + BaseApplication.getInstance().GUID +
                "&timestamp=" + time +
                "&token=" + BaseApplication.getInstance().TOKEN;
        String sign = EncodeByMD5(data);
        cardData.setSign(sign);
        cardData.setTimestamp(time + "");

        L.e("准备请求。。。。。");
//        handler.postDelayed(runnable, 3000);

        if (state == 0) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    getPsd(BaseApplication.getInstance().url1, cardData);
                }
            });
        } else if (state == 1) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    getPsd(BaseApplication.getInstance().url2, cardData1);
                }
            });

        }
    }

    private void getPsd(final String url, final Object data) {
        L.e("请求参数：" + new Gson().toJson(data));

        CachedThreadFactory.getInstance().addTask(System.currentTimeMillis() + "", new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder().
                            protocols(Collections.singletonList(Protocol.HTTP_1_1));
                    OkHttpClient okhttpClient = okHttpBuilder.build();

                    Gson gson = new Gson();

                    MediaType mediaType = MediaType.parse("text/plain");
                    RequestBody body = RequestBody.create(mediaType, gson.toJson(data));

                    Request request = new Request.Builder()
                            .url(url)
                            .method("POST", body)
                            .addHeader("Content-Type", "text/plain")
                            .build();
                    Response response = okhttpClient.newCall(request).execute();
                    String bodyString = response.body().string();
                    L.e("bodyString：" + bodyString);
                    JSONObject object = new JSONObject(bodyString);
                    if (object.getString("errcode").equals("0")) {
                        JSONArray array = object.getJSONArray("data");
                        JSONObject value = array.getJSONObject(0);
                        if (state == 0) {
                            dmcodePsd = value.getString("password");
                            dmcode = value.getString("dmcode");
                            stpcode = value.getString("stpcode");
                            L.e("dmcodePsd=" + dmcodePsd + ",dmcode=" + dmcode + ",stpcode=" + stpcode);

//                            handler.postDelayed(runnable, 500);
                            handler(dmcodePsd, "8", dmcode);
                        } else if (state == 1) {
                            stpcodePsd = value.getString("stppwd");
                            L.e("stpcodepsd=" + stpcodePsd);

//                            handler.postDelayed(runnable, 500);
                            handler(stpcodePsd, "4", stpcode);
                        }
                    }

                } catch (Exception e) {
                    L.e("getPsd error:" + e.toString());
                }
            }
        });


    }

    public Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if (!isReaderSuccess) {
                usbDevice = BaseApplication.getInstance().reader.GetUsbReader();
                if (usbDevice == null) {
//                    L.e("未找到读写器");
                    handler.postDelayed(this, 1000);
                } else {
                    // 判断是否拥有该设备的连接权限
                    if (!BaseApplication.getInstance().manager.hasPermission(usbDevice)) {
                        L.e("请求权限。。。。");
                        // 如果没有则请求权限
                        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(activity, 0,
                                new Intent(Device_USB), PendingIntent.FLAG_UPDATE_CURRENT);
                        BaseApplication.getInstance().manager.requestPermission(usbDevice, mPermissionIntent);

                        handler.postDelayed(this, 1000);
                    } else {
                        short st = BaseApplication.getInstance().reader.OpenReader(usbDevice);
                        if (st >= 0) {
                            L.e("读写器连接成功");
                            isReaderSuccess = true;
                        } else {
                            L.e("读写器连接失败");
                            isReaderSuccess = false;
                            handler.postDelayed(this, 1000);
                        }
                    }
                }
            }

            if (isReaderSuccess) {
                // step1.请求
                byte mode = (byte) 1;
                short st1 = BaseApplication.getInstance().reader.rf_request(mode);

                short st2 = -1;
                if (st1 >= 0) {
                    L.e("请求卡片成功,返回卡类型代码:" + st1);

                    // step2.防冲突
                    Arrays.fill(snr, (byte) 0x00);
                    st2 = BaseApplication.getInstance().reader.rf_anticoll(snr);
                    if (st2 >= 0) {
                        try {
                            cardSnr = BaseApplication.getInstance().reader.bytesToHexString(snr, 0, st2);
                            L.e("防冲突成功，返回卡片序列号:" + cardSnr);

                            // step3.选择卡片
                            short st3 = BaseApplication.getInstance().reader.rf_select(snr);
                            if (st3 >= 0) {
                                L.e("选择卡片成功,返回卡片容量:" + st2);

                                //此刻表示插卡了 ，添加语音提示
                                BaseApplication.getInstance().isCardOn = true;

//                                mediaPlayer = MediaPlayer.create(activity, R.raw.card);
//                                mediaPlayer.start();

                                L.e("state=" + state);
                                if (isWriteCard) {
                                    isWriteCard = false;
                                    updateMoney(stpcodePsd, "5", stpcode, writeCardMoney, writeCardNum);
                                } else {
                                    if (state == 0 || state == 1) {

                                        request();
                                    } else if (state == 2) {
                                        handler(stpcodePsd, "5", stpcode);
                                    } else if (state == 3) {

                                        handler.postDelayed(this, 1000);
                                    }
                                }
                            } else {
                                BaseApplication.getInstance().isCardOn = false;
                                L.e("选择卡片失败:" + BaseApplication.getInstance().reader.GetErrMessage(st2));
                                state = 0;

                                //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                                handler.postDelayed(this, 1000);
                            }


                        } catch (Exception e) {
                            L.e("防冲突失败=====" + e.toString());
                            state = 0;
                            BaseApplication.getInstance().isCardOn = false;
                            //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                            handler.postDelayed(this, 1000);
                        }

                    } else {
                        L.e("防冲突失败:" + BaseApplication.getInstance().reader.GetErrMessage(st2));
                        state = 0;
                        BaseApplication.getInstance().isCardOn = false;
                        //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                        handler.postDelayed(this, 1000);
                    }


                } else {
                    state = 0;
                    BaseApplication.getInstance().isCardOn = false;
//                    isReaderSuccess = false;
                    //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                    handler.postDelayed(this, 1000);
                    L.e("请求卡片失败,返回卡类型代码:" + BaseApplication.getInstance().reader.GetErrMessage(st1));
                }

            }
        }
    };


    /**
     * @param psd     密码
     * @param lineNum 块号
     * @param code    扇区号
     */
    private void handler(String psd, String lineNum, String code) {

        // step4.校验密码
        try {
            //密码
            String strPwd = psd;
            byte[] btPwd = BaseApplication.getInstance().reader.hexStringToBytes(strPwd);
            byte secNo = Byte.parseByte(code);//扇区号
            byte keyMode = (byte) 0;//密码模式
            short st4 = BaseApplication.getInstance().reader.rf_authentication(keyMode, secNo, btPwd);
            if (st4 >= 0) {
                L.e(secNo + "扇区校验密码成功:" + st4);

                //读数据
                try {
                    byte[] data = new byte[16];
                    byte blockNo = Byte.parseByte(lineNum);//块号
                    short st = BaseApplication.getInstance().reader.rf_read(blockNo, data);
                    if (st >= 0) {

                        try {

                            String requestData = BaseApplication.getInstance().reader.bytesToHexString(data, 0, st);
                            L.e(blockNo + "块号数据=" + requestData);
                            L.e("解密后数据=" + hexStringToString(requestData));

                            if (state == 0) {
                                cardData1 = new CardData1();
                                cardData1.setGuid(BaseApplication.getInstance().GUID);
                                int time = (int) (System.currentTimeMillis() / 1000);

                                cardData1.setTimestamp(time + "");
                                cardData1.setPwd(requestData);

                                String data1 = "guid=" + BaseApplication.getInstance().GUID +
                                        "&pwd=" + requestData +
                                        "&timestamp=" + time +
                                        "&token=" + BaseApplication.getInstance().TOKEN;
                                String sign = EncodeByMD5(data1);
                                cardData1.setSign(sign);

                                state = 1;
                                handler.post(runnable);
                            } else if (state == 1) {
                                cardNum = hexStringToString(requestData);

                                state = 2;
                                handler.post(runnable);

                            } else if (state == 2) {
                                cardMoney = hexStringToString(requestData);

                                Common.imei = Util.getIMEI(activity);
                                if (Common.imei != null && !"".equals(Common.imei)) {
                                    PostNex.cardPost(cardNum, cardSnr, cardMoney);
                                }
                                state = 3;
                                handler.post(runnable);
                            }


                        } catch (Exception e) {
                            L.e("E===" + e.toString() + ",,st=" + st);
                            state = 0;
                            BaseApplication.getInstance().isCardOn = false;
                            //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                            handler.postDelayed(runnable, 1000);
                        }
                    } else {
                        L.e("e:" + BaseApplication.getInstance().reader.GetErrMessage(st));
                        state = 0;
                        BaseApplication.getInstance().isCardOn = false;
                        //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                        handler.postDelayed(runnable, 1000);
                    }

                } catch (Exception e) {
                    // TODO: handle exception
                    L.e("e=" + e.toString());

                    state = 0;
                    BaseApplication.getInstance().isCardOn = false;
                    //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                    handler.postDelayed(runnable, 1000);
                }

            } else {
                L.e("校验密码失败:" + BaseApplication.getInstance().reader.GetErrMessage(st4));

                state = 0;
                BaseApplication.getInstance().isCardOn = false;
                //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
                handler.postDelayed(runnable, 1000);
            }

        } catch (Exception e) {
            // TODO: handle exception
            L.e("校验密码报错:" + e.getMessage());
            state = 0;
            BaseApplication.getInstance().isCardOn = false;
            //请求卡片失败 表示没有卡片放到读卡器上 这时候要不断寻卡 直到有卡片放上去
            handler.postDelayed(runnable, 1000);
        }


    }

    /**
     * 修改金额
     *
     * @param psd     密码
     * @param lineNum 块号
     * @param code    扇区号
     */
    private void updateMoney(String psd, String lineNum, String code, String money, String cardNum) {
        if (this.cardNum.equals(cardNum)) {
            // step4.校验密码
            try {
                //密码
                String strPwd = psd;

                byte[] btPwd = BaseApplication.getInstance().reader.hexStringToBytes(strPwd);
                byte secNo = Byte.parseByte(code);//扇区号
                byte keyMode = (byte) 0;//密码模式
                short st4 = BaseApplication.getInstance().reader.rf_authentication(keyMode, secNo, btPwd);
                if (st4 >= 0) {
                    L.e(secNo + "扇区校验密码成功:" + st4);
                } else {
                    L.e("校验密码失败:" + BaseApplication.getInstance().reader.GetErrMessage(st4));
                }

            } catch (Exception e) {
                // TODO: handle exception
                L.e("校验密码报错:" + e.getMessage());
            }

            //写数据
            try {
                byte blockNo = Byte.parseByte(lineNum);//块号
                byte[] writeData = BaseApplication.getInstance().reader.hexStringToBytes(strTo16(money));
                short st = BaseApplication.getInstance().reader.rf_write(blockNo, writeData);
                if (st >= 0) {
                    L.e("写卡成功。。。。。");
                } else {
                    L.e("写卡失败:" + BaseApplication.getInstance().reader.GetErrMessage(st));
                }


            } catch (Exception e) {
                // TODO: handle exception
                L.e("e=" + e.toString());
            }
        } else {
            L.e("卡号不一致，无法写卡===");
        }

    }

    /**
     * 字符串转化成为16进制字符串
     *
     * @param s
     * @return
     */
    public String strTo16(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch);
            str = str + s4;
        }
        return str;
    }

    public String hexStringToString(String s) {
        Matcher matcher = null;
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");

            String regEx = "[^0-9]";

            Pattern p = Pattern.compile(regEx);

            matcher = p.matcher(s);
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return matcher.replaceAll("").trim();
    }


    /**
     * 对字符串进行32位MD5加密
     *
     * @param str 待加密字符串
     * @return
     */
    public String EncodeByMD5(String str) {
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(str.getBytes("UTF-8"));
            // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
            String md5 = new BigInteger(1, md.digest()).toString(16);
            //BigInteger会把0省略掉，需补全至32位
            return fillMD5(md5);
        } catch (Exception e) {
            throw new RuntimeException("MD5加密错误:" + e.getMessage(), e);
        }
    }

    private String fillMD5(String md5) {
        //如果不够32位则回调自身补零，最后返回32位长度的签名
        return md5.length() == 32 ? md5 : fillMD5("0" + md5);
    }

}
